package com.ruoyi.data.service.impl;

import cn.hutool.core.bean.BeanUtil;
import cn.hutool.core.collection.CollUtil;
import cn.hutool.core.date.DateField;
import cn.hutool.core.date.DateUtil;
import cn.hutool.core.util.ObjectUtil;
import cn.hutool.http.HttpUtil;
import com.baomidou.mybatisplus.core.conditions.update.LambdaUpdateWrapper;
import com.ruoyi.common.enums.ServiceStatus;
import com.ruoyi.common.helper.LoginHelper;
import com.ruoyi.common.utils.StringUtils;
import com.ruoyi.common.core.page.TableDataInfo;
import com.ruoyi.common.core.domain.PageQuery;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.core.toolkit.Wrappers;
import com.ruoyi.data.domain.DevicesOld;
import com.ruoyi.data.domain.bo.Devices.*;
import com.ruoyi.data.domain.vo.Devices.SelectTypeUserVo;
import com.ruoyi.data.domain.vo.Devices.SelectTypeVo;
import com.ruoyi.data.domain.vo.Devices.StatisticsVo;
import com.ruoyi.data.domain.vo.DevicesOldVo;
import com.ruoyi.data.mapper.DevicesOldMapper;
import lombok.RequiredArgsConstructor;
import org.springframework.stereotype.Service;
import com.ruoyi.data.domain.vo.Devices.DevicesVo;
import com.ruoyi.data.domain.Devices;
import com.ruoyi.data.mapper.DevicesMapper;
import com.ruoyi.data.service.IDevicesService;

import java.util.*;
import java.util.stream.Collectors;

/**
 * 设备Service业务层处理
 *
 * @author ruoyi
 * @date 2024-10-12
 */
@RequiredArgsConstructor
@Service
public class DevicesServiceImpl implements IDevicesService {

    private final DevicesMapper baseMapper;


    private final DevicesOldMapper oldBaseMapper;

    /**
     * 查询设备列表
     */
    @Override
    public TableDataInfo<DevicesVo> queryPageList(DevicesBo bo, PageQuery pageQuery) {

        Page<DevicesVo> result  = new Page<>();

        //如果日期是今天
        if (StringUtils.equals(DateUtil.today(), DateUtil.format(bo.getSelectDate(), "yyyy-MM-dd")) || bo.getSelectDate() == null) {
            result = baseMapper.selectVoPage(pageQuery.build(), buildQueryWrapper(bo));

        } else {
            Page<DevicesOldVo> devicesOldVos = oldBaseMapper.selectVoPage(pageQuery.build(), buildOldQueryWrapper(bo));
            result.setRecords(BeanUtil.copyToList(devicesOldVos.getRecords(), DevicesVo.class));
            result.setPages(devicesOldVos.getPages());
            result.setCurrent(devicesOldVos.getCurrent());
            result.setTotal(devicesOldVos.getTotal());
        }

        return TableDataInfo.build(result);
    }

    /**
     * 查询设备列表
     */
    @Override
    public List<DevicesVo> queryList(DevicesBo bo) {

        List<DevicesVo> devicesVoList = new ArrayList<>();

        //如果日期是今天
        if (StringUtils.equals(DateUtil.today(), DateUtil.format(bo.getSelectDate(), "yyyy-MM-dd")) || bo.getSelectDate() == null) {
            devicesVoList = baseMapper.selectVoList(buildQueryWrapper(bo));

        } else {
            List<DevicesOldVo> devicesOldVos = oldBaseMapper.selectVoList(buildOldQueryWrapper(bo));

            BeanUtil.copyToList(devicesOldVos, DevicesVo.class);
        }

        return devicesVoList;
    }

    private static String getOneMinutebeforeTime(int offset) {
        Date newDate = DateUtil.offset(new Date(), DateField.MINUTE, offset);
        String startTime = DateUtil.format(newDate, "yyyy-MM-dd HH:mm:ss");
        return startTime;
    }


    private LambdaQueryWrapper<Devices> buildQueryWrapper(DevicesBo bo) {
        Long userId = LoginHelper.getUserId();

        LambdaQueryWrapper<Devices> lqw = Wrappers.lambdaQuery();
        lqw.eq(!LoginHelper.isAdmin(), Devices::getUserId, userId);
        lqw.eq(StringUtils.isNotEmpty(bo.getUserId()), Devices::getUserId, bo.getUserId());

        if (StringUtils.isNotBlank(bo.getDeviceGroup())) {
            lqw.in(Devices::getDeviceGroup, CollUtil.toList(bo.getDeviceGroup().split(",")));
        }
        lqw.eq(StringUtils.isNotBlank(bo.getProject()), Devices::getProject, bo.getProject());
        lqw.eq(bo.getSelectDate() != null, Devices::getDate, bo.getSelectDate());

        //lqw.orderByAsc(Devices::getDeviceName);

        // 在线设备是判断”last_update”时间字段，1分钟内更新,"status"字段=0，
        if (StringUtils.equals(bo.getServiceState(), ServiceStatus.online.getCode())) {
            lqw.ge(Devices::getLastUpdate, getOneMinutebeforeTime(-1));
            lqw.le(Devices::getLastUpdate, getOneMinutebeforeTime(0));

            lqw.eq(Devices::getStatus, 0L);
            lqw.last("ORDER BY LENGTH(device_name), device_name");

        }
        //  离线设备是判断"last_update”时间字段 1分钟 没更新,"status"字段=0，
        else if (StringUtils.equals(bo.getServiceState(), ServiceStatus.offline.getCode())) {
            lqw.and(w -> w.lt(Devices::getLastUpdate, getOneMinutebeforeTime(-1)).or().gt(Devices::getLastUpdate, getOneMinutebeforeTime(0)));

            lqw.eq(Devices::getStatus, 0L);
            lqw.last("ORDER BY last_update");

        }
        //  已启动是判断”isSeriptRunnins”字段=1,并且是在线设备，
        else if (StringUtils.equals(bo.getServiceState(), ServiceStatus.started.getCode())) {
            //lqw.eq(Devices::getStatus, 0L);
            lqw.ge(Devices::getLastUpdate, getOneMinutebeforeTime(-1));
            lqw.le(Devices::getLastUpdate, getOneMinutebeforeTime(0));
            lqw.eq(Devices::getIsScriptRunning, 1);
            lqw.last("ORDER BY LENGTH(device_name), device_name");

        }
        //未启动是判断"isscriptRumming"时间字段 =0,并且是在线设备  （status：0未删除，1是删除）
        else if (StringUtils.equals(bo.getServiceState(), ServiceStatus.notStarted.getCode())) {
            //lqw.eq(Devices::getStatus, 0L);
            lqw.ge(Devices::getLastUpdate, getOneMinutebeforeTime(-1));
            lqw.le(Devices::getLastUpdate, getOneMinutebeforeTime(0));
            lqw.eq(Devices::getIsScriptRunning, 0);
            lqw.last("ORDER BY LENGTH(device_name), device_name");

        }
        //判断是否异常
        else if (StringUtils.equals(bo.getServiceState(), ServiceStatus.deviceException.getCode())) {
            lqw.eq(Devices::getIsException, 1);
            lqw.eq(Devices::getStatus, 0L);
        }
        else {
            lqw.eq(Devices::getStatus, 0L);
            lqw.last("ORDER BY LENGTH(device_name), device_name");
        }


        return lqw;
    }

    private LambdaQueryWrapper<DevicesOld> buildOldQueryWrapper(DevicesBo bo) {
        Long userId = LoginHelper.getUserId();


        LambdaQueryWrapper<DevicesOld> lqw = Wrappers.lambdaQuery();
        lqw.eq(StringUtils.isNotBlank(bo.getProject()), DevicesOld::getProject, bo.getProject());

        if (StringUtils.isNotBlank(bo.getDeviceGroup())) {
            lqw.in(DevicesOld::getDeviceGroup, CollUtil.toList(bo.getDeviceGroup().split(",")));
        }
        lqw.eq(bo.getSelectDate() != null, DevicesOld::getDate, bo.getSelectDate());

        lqw.eq(!LoginHelper.isAdmin(), DevicesOld::getUserId, userId);
        lqw.eq(StringUtils.isNotEmpty(bo.getUserId()), DevicesOld::getUserId, bo.getUserId());

        lqw.orderByAsc(DevicesOld::getDeviceName);

        return lqw;
    }




    /**
     * 修改设备
     */
    @Override
    public Boolean updateByBo(List<UpdateDevicesBo> bo) {
        List<Devices> devices = BeanUtil.copyToList(bo, Devices.class);

        devices.forEach(update -> {
            baseMapper.update(update, new LambdaUpdateWrapper<Devices>().in(Devices::getDeviceId, update.getDeviceId()));
        });

        return true;
    }


    /**
     * 批量删除设备
     */
    @Override
    public Boolean deleteWithValidByIds(Collection<String> ids, Boolean isValid) {
        Devices devices = new Devices();
        devices.setStatus(1L);

        return baseMapper.update(devices, new LambdaUpdateWrapper<Devices>().in(Devices::getDeviceId, ids)) > 0;
    }

    @Override
    public List<SelectTypeVo> selectTypeList(SelectTypeBo bo) {
        Long userId = LoginHelper.getUserId();

        if (StringUtils.equals(DateUtil.today(), DateUtil.format(bo.getSelectDate(), "yyyy-MM-dd")) || bo.getSelectDate() == null) {

            LambdaQueryWrapper<Devices> lqw = new LambdaQueryWrapper<>();
            lqw.eq(!LoginHelper.isAdmin(), Devices::getUserId, userId);
            lqw.select(Devices::getProject, Devices::getDeviceGroup, Devices::getDeviceId, Devices::getDeviceName, Devices::getUserId, Devices::getUserName,
                    Devices::getStatus, Devices::getLastUpdate);
            lqw.eq(StringUtils.isNotEmpty(bo.getProject()), Devices::getProject, bo.getProject());

            if (StringUtils.equals(bo.getType(), "project")){
                lqw.like(StringUtils.isNotEmpty(bo.getName()), Devices::getProject, bo.getName());
                Map<String, List<Devices>> map = baseMapper.selectList(lqw).stream().collect(Collectors.groupingBy(Devices::getProject));

                lqw.ge(Devices::getLastUpdate, getOneMinutebeforeTime(-1));
                lqw.le(Devices::getLastUpdate, getOneMinutebeforeTime(0));
                lqw.eq(Devices::getStatus, 0L);
                Map<String, List<Devices>> map2 = baseMapper.selectList(lqw).stream().collect(Collectors.groupingBy(Devices::getProject));

                return formattingData(map, map2);
            }
            // deviceGroup
            else {
                lqw.like(StringUtils.isNotEmpty(bo.getName()), Devices::getDeviceGroup, bo.getName());
                Map<String, List<Devices>> map = baseMapper.selectList(lqw).stream().collect(Collectors.groupingBy(Devices::getDeviceGroup));

                lqw.ge(Devices::getLastUpdate, getOneMinutebeforeTime(-1));
                lqw.le(Devices::getLastUpdate, getOneMinutebeforeTime(0));
                lqw.eq(Devices::getStatus, 0L);
                Map<String, List<Devices>> map2 = baseMapper.selectList(lqw).stream().collect(Collectors.groupingBy(Devices::getDeviceGroup));

                return formattingData(map, map2);
            }

        } else {
            LambdaQueryWrapper<DevicesOld> lqw = new LambdaQueryWrapper<>();
            lqw.eq(!LoginHelper.isAdmin(), DevicesOld::getUserId, userId);
            lqw.select(DevicesOld::getDeviceGroup, DevicesOld::getProject, DevicesOld::getDeviceId,DevicesOld::getDeviceName, DevicesOld::getUserId, DevicesOld::getUserName,
                    DevicesOld::getStatus, DevicesOld::getLastUpdate);
            lqw.eq(StringUtils.isNotEmpty(bo.getProject()), DevicesOld::getProject, bo.getProject());
            lqw.eq(bo.getSelectDate() != null, DevicesOld::getDate, bo.getSelectDate());

            if (StringUtils.equals(bo.getType(), "project")){
                lqw.like(StringUtils.isNotEmpty(bo.getName()), DevicesOld::getProject, bo.getName());
                Map<String, List<Devices>> map = BeanUtil.copyToList(oldBaseMapper.selectList(lqw), Devices.class).stream().collect(Collectors.groupingBy(Devices::getProject));

                lqw.ge(DevicesOld::getLastUpdate, getOneMinutebeforeTime(-1));
                lqw.le(DevicesOld::getLastUpdate, getOneMinutebeforeTime(0));
                lqw.eq(DevicesOld::getStatus, 0L);
                Map<String, List<Devices>> map2 =  BeanUtil.copyToList(oldBaseMapper.selectList(lqw), Devices.class).stream().collect(Collectors.groupingBy(Devices::getProject));

                return formattingData(map, map2);
            }
            // deviceGroup
            else {
                lqw.like(StringUtils.isNotEmpty(bo.getName()), DevicesOld::getDeviceGroup, bo.getName());
                Map<String, List<Devices>> map = BeanUtil.copyToList(oldBaseMapper.selectList(lqw), Devices.class).stream().collect(Collectors.groupingBy(Devices::getDeviceGroup));

                lqw.ge(DevicesOld::getLastUpdate, getOneMinutebeforeTime(-1));
                lqw.le(DevicesOld::getLastUpdate, getOneMinutebeforeTime(0));
                lqw.eq(DevicesOld::getStatus, 0L);
                Map<String, List<Devices>> map2 =  BeanUtil.copyToList(oldBaseMapper.selectList(lqw), Devices.class).stream().collect(Collectors.groupingBy(Devices::getDeviceGroup));

                return formattingData(map, map2);
            }
        }
    }

    private List<SelectTypeVo> formattingData(Map<String, List<Devices>> map, Map<String, List<Devices>> map2){
        List<SelectTypeVo> list = new ArrayList<>();
        for (String key : map.keySet()){
            SelectTypeVo selectTypeVo = new SelectTypeVo();
            selectTypeVo.setName(key);
            List<Devices> devices = map2.get(key);
            List<SelectTypeUserVo> res = new ArrayList<>();

            if (devices == null){
                list.add(selectTypeVo);
                continue;
            }

            //devices 取出DeviceName截取-后的数字后按截取数字从小到大排序
            devices.sort(Comparator.comparing(Devices::getDeviceName));

            for (Devices device : devices){
                SelectTypeUserVo v = new SelectTypeUserVo();

                v.setId(device.getDeviceId());
                v.setName(device.getDeviceName());
                v.setUserId(device.getUserId());
                v.setUserName(device.getUserName());
                res.add(v);
            }
            selectTypeVo.setDeviceIdList(res);
            if (!devices.isEmpty()){
                selectTypeVo.setUserId(devices.get(0).getUserId());
                selectTypeVo.setUserName(devices.get(0).getUserName());
            }
            list.add(selectTypeVo);
        }


        return list;
    }

    @Override
    public StatisticsVo queryingStatistics(StatisticsBo bo) {
        StatisticsVo statisticsVo = new StatisticsVo();
        Long userId = LoginHelper.getUserId();

        if (StringUtils.isNotEmpty(bo.getUserId())){
            userId = Long.parseLong(bo.getUserId());
        }


        //.apply("TIMESTAMPDIFF(SECOND, last_update, NOW()) <= {0}","60")
        if (StringUtils.equals(DateUtil.today(), DateUtil.format(bo.getSelectDate(), "yyyy-MM-dd")) || bo.getSelectDate() == null) {
            statisticsVo.setOnlineCount(
                baseMapper.selectCount(
                    new LambdaQueryWrapper<Devices>()
                        .eq(StringUtils.isNotBlank(bo.getProject()), Devices::getProject, bo.getProject())
                        .ge(Devices::getLastUpdate, getOneMinutebeforeTime(-1))
                        .le(Devices::getLastUpdate, getOneMinutebeforeTime(0))
                        .eq(Devices::getStatus, 0L)
                        .eq(bo.getSelectDate() != null, Devices::getDate, bo.getSelectDate())
                        .eq(!LoginHelper.isAdmin(), Devices::getUserId, userId)
                )
            );
            statisticsVo.setOfflineCount(
                baseMapper.selectCount(
                    new LambdaQueryWrapper<Devices>()
                        .eq(StringUtils.isNotBlank(bo.getProject()), Devices::getProject, bo.getProject())
                            .and(w -> w.lt(Devices::getLastUpdate, getOneMinutebeforeTime(-1)).or().gt(Devices::getLastUpdate, getOneMinutebeforeTime(0)))
                        .eq(Devices::getStatus, 0L)
                            .eq(bo.getSelectDate() != null, Devices::getDate, bo.getSelectDate())
                        .eq(!LoginHelper.isAdmin(), Devices::getUserId, userId)
                )
            );
            statisticsVo.setStartedCount(
                baseMapper.selectCount(
                    new LambdaQueryWrapper<Devices>()
                        .eq(StringUtils.isNotBlank(bo.getProject()), Devices::getProject, bo.getProject())
                            .ge(Devices::getLastUpdate, getOneMinutebeforeTime(-1))
                            .le(Devices::getLastUpdate, getOneMinutebeforeTime(0))
                        //.eq(Devices::getStatus, 0L)
                        .eq(Devices::getIsScriptRunning, 1)
                        .eq(bo.getSelectDate() != null, Devices::getDate, bo.getSelectDate())
                        .eq(!LoginHelper.isAdmin(), Devices::getUserId, userId)
                )
            );
            statisticsVo.setNotStartedCount(
                baseMapper.selectCount(
                    new LambdaQueryWrapper<Devices>()
                        .eq(StringUtils.isNotBlank(bo.getProject()), Devices::getProject, bo.getProject())
                            .ge(Devices::getLastUpdate, getOneMinutebeforeTime(-1))
                            .le(Devices::getLastUpdate, getOneMinutebeforeTime(0))
                        //.eq(Devices::getStatus, 0L)
                        .eq(Devices::getIsScriptRunning, 0)
                        .eq(bo.getSelectDate() != null, Devices::getDate, bo.getSelectDate())
                        .eq(!LoginHelper.isAdmin(), Devices::getUserId, userId)
                )
            );
        } else {
            statisticsVo.setOnlineCount(
                    oldBaseMapper.selectCount(
                            new LambdaQueryWrapper<DevicesOld>()
                                    .eq(StringUtils.isNotBlank(bo.getProject()), DevicesOld::getProject, bo.getProject())
                                    .ge(DevicesOld::getLastUpdate, getOneMinutebeforeTime(-1))
                                    .le(DevicesOld::getLastUpdate, getOneMinutebeforeTime(0))
                                    .eq(DevicesOld::getStatus, 0L)
                                    .eq(bo.getSelectDate() != null, DevicesOld::getDate, bo.getSelectDate())
                                    .eq(!LoginHelper.isAdmin(), DevicesOld::getUserId, userId)
                    )
            );
            statisticsVo.setOfflineCount(
                    oldBaseMapper.selectCount(
                            new LambdaQueryWrapper<DevicesOld>()
                                    .eq(StringUtils.isNotBlank(bo.getProject()), DevicesOld::getProject, bo.getProject())
                                    .and(w -> w.lt(DevicesOld::getLastUpdate, getOneMinutebeforeTime(-1)).or().gt(DevicesOld::getLastUpdate, getOneMinutebeforeTime(0)))
                                    .eq(DevicesOld::getStatus, 0L)
                                    .eq(bo.getSelectDate() != null, DevicesOld::getDate, bo.getSelectDate())
                                    .eq(!LoginHelper.isAdmin(), DevicesOld::getUserId, userId)
                    )
            );
            statisticsVo.setStartedCount(
                oldBaseMapper.selectCount(
                    new LambdaQueryWrapper<DevicesOld>()
                        .eq(StringUtils.isNotBlank(bo.getProject()), DevicesOld::getProject, bo.getProject())
                            .ge(DevicesOld::getLastUpdate, getOneMinutebeforeTime(-1))
                            .le(DevicesOld::getLastUpdate, getOneMinutebeforeTime(0))
                        //.eq(DevicesOld::getStatus, 0L)
                        .eq(DevicesOld::getIsScriptRunning, 1)
                        .eq(bo.getSelectDate() != null, DevicesOld::getDate, bo.getSelectDate())
                        .eq(!LoginHelper.isAdmin(), DevicesOld::getUserId, userId)
                )
            );
            statisticsVo.setNotStartedCount(
                oldBaseMapper.selectCount(
                    new LambdaQueryWrapper<DevicesOld>()
                        .eq(StringUtils.isNotBlank(bo.getProject()), DevicesOld::getProject, bo.getProject())
                            .ge(DevicesOld::getLastUpdate, getOneMinutebeforeTime(-1))
                            .le(DevicesOld::getLastUpdate, getOneMinutebeforeTime(0))
                        //.eq(DevicesOld::getStatus, 0L)
                        .eq(DevicesOld::getIsScriptRunning, 0)
                        .eq(bo.getSelectDate() != null, DevicesOld::getDate, bo.getSelectDate())
                        .eq(!LoginHelper.isAdmin(), DevicesOld::getUserId, userId)
                )
            );
        }


        return statisticsVo;
    }

    @Override
    public List<DevicesVo> noPageList(SelectNoPageBo bo) {
        return baseMapper.selectVoList(new LambdaQueryWrapper<Devices>().in(Devices::getDeviceId, bo.getDeviceIdList()));
    }

    @Override
    public String getPhpCount(StatisticsBo bo) {

        HashMap<String,Object> map = new HashMap<>();
        map.put("user_id", LoginHelper.getUserId());
        map.put("project", bo.getProject());
        map.put("date", DateUtil.format(bo.getSelectDate(), "yyyy-MM-dd"));
        map.put("deviceGroup", bo.getDeviceGroup());


        return HttpUtil.get("https://api.wtytl.com:9001/count.php", map);
    }
}
